home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / interapplication comm / moreosl / moredebugging / morebblog.c next >
Encoding:
Text File  |  2000-06-23  |  21.9 KB  |  749 lines

  1. /*
  2.     File:        MoreBBLog.c
  3.  
  4.     Contains:    Module to log debug traces to BBEdit.
  5.  
  6.     Written by:    Quinn
  7.  
  8.     Copyright:    Copyright © 2000 by Apple Computer, Inc., all rights reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.  
  20.          <4>     27/3/00    Quinn   Add some more coercions from basic types to text.  Don't assert
  21.                                     if we can't AESend to BBEdit because BBEdit isn't running.
  22.          <3>     20/3/00    Quinn   Added coercions from built-in types to typeText to make
  23.                                     descriptor logging more useful.  Changed BBLogDesc to bypass
  24.                                     Str255 limit.
  25.          <2>      3/9/00    gaw     API changes for MoreAppleEvents
  26.          <1>      6/3/00    Quinn   First checked in.
  27. */
  28.  
  29. /////////////////////////////////////////////////////////////////
  30.  
  31. // MoreIsBetter Setup
  32.  
  33. #include "MoreSetup.h"
  34.  
  35. // Mac OS Interfaces
  36.  
  37. #include <AERegistry.h>
  38. #include <PLStringFuncs.h>
  39. #include <TextUtils.h>
  40. #include <Aliases.h>
  41.  
  42. // MIB Prototypes
  43.  
  44. #include "MoreAppleEvents.h"
  45. #include "MoreMemory.h"
  46.  
  47. // Our Prototypes
  48.  
  49. #include "MoreBBLog.h"
  50.  
  51. /////////////////////////////////////////////////////////////////
  52.  
  53. #if MORE_DEBUG
  54.  
  55.     static AECoercePtrUPP gBasicToTextCoerceUPP;            // -> BasicToTextCoerceProc
  56.     
  57.     static pascal OSErr BasicToTextCoerceProc(DescType typeCode, const void *dataPtr, Size dataSize, DescType toType, SInt32 handlerRefCon, AEDesc *result)
  58.         // A coercion handler to convert some basic Apple event types
  59.         // to text.  I added these types on demand, as I noticed that my
  60.         // MoreOSL test program needed them.
  61.     {
  62.         #pragma unused(handlerRefCon)
  63.         OSStatus        err;
  64.         ccntTokenRecPtr tokenData;
  65.         AEDesc          tokenAsText;
  66.         Handle          resultH;
  67.         AliasHandle     aliasH;
  68.         Str63            tmpStr63;
  69.         Str255            tmpStr;
  70.         Str255            tmpStr2;
  71.         FSSpecPtr         fssP;
  72.         
  73.         MoreAssertQ(toType == typeText);
  74.         MoreAssertQ(result != nil);
  75.         
  76.         MoreAENullDesc(result);
  77.         
  78.         switch (typeCode) {
  79.             case typeNull:
  80.             case typeCurrentContainer:
  81.             case typeObjectBeingExamined:
  82.                 // You’d think that the following assert would be useful, but it fires all the time.
  83.                 // MoreAssertQ(dataPtr == nil);
  84.                 MoreAssertQ(dataSize == 0);
  85.                 err = AECreateDesc(typeText, &typeCode, sizeof(typeCode), result);
  86.                 break;
  87.                 
  88.             case typeToken:
  89.                 MoreAssertQ(dataSize == sizeof(ccntTokenRecord));
  90.                 MoreAssertQ(dataPtr  != nil);
  91.                 
  92.                 // The data for a token is a ccntTokenRecord.  We extract the data,
  93.                 // then coerce the embedded descriptor to text, then add a header
  94.                 // that identifies the token as a token.
  95.                 
  96.                 MoreAENullDesc(&tokenAsText);
  97.                 resultH = nil;
  98.                 
  99.                 tokenData = (ccntTokenRecPtr) dataPtr;
  100.                 err = AECoerceDesc(&tokenData->token, typeText, &tokenAsText);
  101.                 if (err == noErr) {
  102.                     err = MoreAECopyDescriptorDataToHandle(&tokenAsText, &resultH);
  103.                 }
  104.                 if (err == noErr) {
  105.                     // Add the string "'toke-'" to the front of the handle.
  106.                     (void) Munger(resultH, 0, nil, 0, "'toke'-", 7);
  107.                     err = MemError();
  108.                 }
  109.                 if (err == noErr) {
  110.                     HLock(resultH);
  111.                     err = AECreateDesc(typeText, *resultH, GetHandleSize(resultH), result);
  112.                 }
  113.                 
  114.                 MoreAEDisposeDesc(&tokenAsText);
  115.                 if (resultH != nil) {
  116.                     DisposeHandle(resultH);
  117.                     MoreAssertQ(MemError() == noErr);
  118.                 }
  119.                 break;
  120.             
  121.             case typeAbsoluteOrdinal:
  122.                 MoreAssertQ(dataSize == sizeof(DescType));
  123.                 err = AECreateDesc(typeText, dataPtr, sizeof(DescType), result);
  124.                 break;
  125.             
  126.             case typeAlias:
  127.                 aliasH = nil;
  128.                 
  129.                 err = PtrToHand(dataPtr, (Handle *) &aliasH, dataSize);
  130.                 if (err == noErr) {
  131.                     err = GetAliasInfo(aliasH, asiAliasName, tmpStr63);
  132.                 }
  133.                 if (err == noErr) {
  134.                     PLstrcpy(tmpStr, "\palias(");
  135.                     PLstrcat(tmpStr, tmpStr63);
  136.                     PLstrcat(tmpStr, "\p)");
  137.                     err = AECreateDesc(typeText, &tmpStr[1], tmpStr[0], result);
  138.                 }
  139.                 
  140.                 if (aliasH != nil) {
  141.                     DisposeHandle( (Handle) aliasH );
  142.                 }
  143.                 break;
  144.  
  145.             case typeFSS:
  146.                 MoreAssertQ(dataPtr != nil);
  147.                 MoreAssertQ(dataSize == sizeof(FSSpec));
  148.                 
  149.                 fssP = (FSSpecPtr) dataPtr;
  150.  
  151.                 PLstrcpy(tmpStr, "\pfss(");
  152.                 PLstrcat(tmpStr, fssP->name);
  153.                 PLstrcat(tmpStr, "\p)");
  154.  
  155.                 err = AECreateDesc(typeText, &tmpStr[1], tmpStr[0], result);
  156.                 break;
  157.  
  158.             case typeBoolean:
  159.                 if ( *((Boolean *) dataPtr) ) {
  160.                     err = AECreateDesc(typeText, "true", 4, result);
  161.                 } else {
  162.                     err = AECreateDesc(typeText, "false", 5, result);
  163.                 }
  164.                 break;
  165.  
  166.             case typeTrue:
  167.                 err = AECreateDesc(typeText, "typeTrue", 8, result);
  168.                 break;
  169.  
  170.             case typeFalse:
  171.                 err = AECreateDesc(typeText, "typeFalse", 9, result);
  172.                 break;
  173.                 
  174.             case typeQDPoint:
  175.                 PLstrcpy(tmpStr, "\pPoint(");
  176.                 NumToString( ((PointPtr) dataPtr)->h, tmpStr2);
  177.                 PLstrcat(tmpStr, tmpStr2);
  178.                 PLstrcat(tmpStr, "\p, ");
  179.                 NumToString( ((PointPtr) dataPtr)->v, tmpStr2);
  180.                 PLstrcat(tmpStr, tmpStr2);
  181.                 PLstrcat(tmpStr, "\p)");
  182.  
  183.                 err = AECreateDesc(typeText, &tmpStr[1], tmpStr[0], result);
  184.                 break;
  185.                 
  186.             case typeQDRectangle:
  187.                 PLstrcpy(tmpStr, "\pRect(");
  188.                 NumToString( ((RectPtr) dataPtr)->left, tmpStr2);
  189.                 PLstrcat(tmpStr, tmpStr2);
  190.                 PLstrcat(tmpStr, "\p, ");
  191.                 NumToString( ((RectPtr) dataPtr)->top, tmpStr2);
  192.                 PLstrcat(tmpStr, tmpStr2);
  193.                 PLstrcat(tmpStr, "\p, ");
  194.                 NumToString( ((RectPtr) dataPtr)->right, tmpStr2);
  195.                 PLstrcat(tmpStr, tmpStr2);
  196.                 PLstrcat(tmpStr, "\p, ");
  197.                 NumToString( ((RectPtr) dataPtr)->bottom, tmpStr2);
  198.                 PLstrcat(tmpStr, tmpStr2);
  199.                 PLstrcat(tmpStr, "\p)");
  200.  
  201.                 err = AECreateDesc(typeText, &tmpStr[1], tmpStr[0], result);
  202.                 break;
  203.  
  204.             default:
  205.                 MoreAssertQ(false);
  206.                 err = errAECoercionFail;
  207.                 break;
  208.         }
  209.         
  210.         return err;
  211.     }
  212.  
  213.     static AECoerceDescUPP gRecordToTextCoerceUPP;            // -> RecordToTextCoerceProc
  214.     
  215.     static pascal OSErr RecordToTextCoerceProc(const AEDesc *fromDesc, DescType toType, long handlerRefCon, AEDesc *toDesc)
  216.         // A coercion handler to convert lists, records, and other things
  217.         // coercible to records, to text.
  218.     {
  219.         #pragma unused(handlerRefCon)
  220.         OSStatus  err;
  221.         Handle    result;
  222.         AEDesc    fromDescAsRecord;
  223.         SInt32    itemIndex;
  224.         SInt32    itemCount;
  225.         AEKeyword thisKeyword;
  226.         AEDesc    thisElement;
  227.         AEDesc    thisElementAsText;
  228.         Size      textSize;
  229.  
  230.         MoreAssertQ(fromDesc != nil);
  231.         MoreAssertQ(toType == typeText);
  232.         MoreAssertQ(toDesc   != nil);
  233.  
  234.         MoreAENullDesc(toDesc);
  235.         MoreAENullDesc(&fromDescAsRecord);
  236.         
  237.         // Create a handle for the resulting text.
  238.         
  239.         result = NewHandle(0);
  240.         err = MoreMemError(result);
  241.         
  242.         // If the incoming data is a record or a list, we handle it natively
  243.         // so just duplicate it into the fromDescAsRecord.  OTOH, if the incoming
  244.         // data is something weird (an object specifier, say), use AECoerceDesc to 
  245.         // coerce it to a record.
  246.         
  247.         if (err == noErr) {
  248.             if (fromDesc->descriptorType == typeAERecord || fromDesc->descriptorType == typeAEList) {
  249.                 err = AEDuplicateDesc(fromDesc, &fromDescAsRecord);
  250.             } else {
  251.                 err = AECoerceDesc(fromDesc, typeAERecord, &fromDescAsRecord);
  252.             }
  253.         }
  254.         
  255.         // Iterate through each element in the record/list, coercing the element
  256.         // to text and appending its text to the handle.
  257.         
  258.         if (err == noErr) {
  259.             err = AECountItems(&fromDescAsRecord, &itemCount);
  260.         }
  261.         if (err == noErr) {
  262.             err = PtrAndHand("{", result, 1);
  263.             for (itemIndex = 1; itemIndex <= itemCount; itemIndex++) {
  264.                 MoreAENullDesc(&thisElement);
  265.                 MoreAENullDesc(&thisElementAsText);
  266.  
  267.                 err = AEGetNthDesc(&fromDescAsRecord, itemIndex, typeWildCard, &thisKeyword, &thisElement);
  268.                 if (err == noErr) {
  269.                     err = AECoerceDesc(&thisElement, typeText, &thisElementAsText);
  270.                     // While writing this code, I spent lots of time figuring out
  271.                     // exactly what type of data couldn’t be coerced to text and then
  272.                     // filling in that particular coercion.  The following assert
  273.                     // helps detect these problems quickly.
  274.                     MoreAssertQ(err != errAECoercionFail);
  275.                 }
  276.                 
  277.                 // If we’re iteratintg through a record, the keyword is meaningful
  278.                 // so add it to the output handle.
  279.                 
  280.                 if (err == noErr && fromDescAsRecord.descriptorType == typeAERecord) {
  281.                     err = PtrAndHand("'", result, 1);
  282.                     if (err == noErr) {
  283.                         err = PtrAndHand(&thisKeyword, result, sizeof(thisKeyword));
  284.                     }
  285.                     if (err == noErr) {
  286.                         err = PtrAndHand("':", result, 2);
  287.                     }
  288.                 }
  289.                 
  290.                 // Grow the handle to make room for the text for this element,
  291.                 // then copy the text to the handle.
  292.                 
  293.                 if (err == noErr) {
  294.                     textSize = AEGetDescDataSize(&thisElementAsText);
  295.                     SetHandleSize(result, GetHandleSize(result) + textSize);
  296.                     err = MemError();
  297.                 }
  298.                 if (err == noErr) {
  299.                     HLock(result);
  300.                     err = AEGetDescData(&thisElementAsText, (*result) + GetHandleSize(result) - textSize, textSize);
  301.                     HUnlock(result);
  302.                 }
  303.                 
  304.                 // If we’re not the last element, add a separator.
  305.                 
  306.                 if (err == noErr && itemIndex < itemCount) {
  307.                     err = PtrAndHand(", ", result, 2);
  308.                 }
  309.                 
  310.                 MoreAEDisposeDesc(&thisElement);
  311.                 MoreAEDisposeDesc(&thisElementAsText);
  312.                 
  313.                 if (err != noErr) {
  314.                     break;
  315.                 }
  316.             }
  317.         }
  318.         if (err == noErr) {
  319.             err = PtrAndHand("}", result, 1);
  320.         }
  321.         
  322.         // Create a descriptor containing the data from the text handle.
  323.         
  324.         if (err == noErr) {
  325.             HLock(result);
  326.             err = AECreateDesc(typeText, *result, GetHandleSize(result), toDesc);
  327.         }
  328.         
  329.         // Clean up.
  330.         
  331.         if (result != nil) {
  332.             DisposeHandle(result);
  333.             MoreAssertQ(MemError() == noErr);
  334.         }
  335.         MoreAEDisposeDesc(&fromDescAsRecord);
  336.         
  337.         return err;
  338.     }
  339.  
  340.     static void InstallTextCoercionHandlers(void)
  341.         // This routine installs a bunch of coercion handles for
  342.         // coercing various common data types to text.  By adding these
  343.         // coercions, we increase the utility of the rest of MoreBBLog, 
  344.         // which relies on being able to coerce various descriptors to text.
  345.     {
  346.         OSStatus junk;
  347.  
  348.         // First install all the basic coercions.
  349.                 
  350.         gBasicToTextCoerceUPP = NewAECoercePtrUPP(BasicToTextCoerceProc);
  351.         MoreAssertQ(gBasicToTextCoerceUPP != nil);
  352.  
  353.         junk = AEInstallCoercionHandler(typeNull, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  354.         MoreAssertQ(junk == noErr);
  355.  
  356.         junk = AEInstallCoercionHandler(typeObjectBeingExamined, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  357.         MoreAssertQ(junk == noErr);
  358.  
  359.         junk = AEInstallCoercionHandler(typeCurrentContainer, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  360.         MoreAssertQ(junk == noErr);
  361.  
  362.         junk = AEInstallCoercionHandler(typeToken, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  363.         MoreAssertQ(junk == noErr);
  364.  
  365.         junk = AEInstallCoercionHandler(typeAbsoluteOrdinal, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  366.         MoreAssertQ(junk == noErr);
  367.  
  368.         junk = AEInstallCoercionHandler(typeAlias, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  369.         MoreAssertQ(junk == noErr);
  370.  
  371.         junk = AEInstallCoercionHandler(typeFSS, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  372.         MoreAssertQ(junk == noErr);
  373.  
  374.         junk = AEInstallCoercionHandler(typeBoolean, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  375.         MoreAssertQ(junk == noErr);
  376.  
  377.         junk = AEInstallCoercionHandler(typeTrue, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  378.         MoreAssertQ(junk == noErr);
  379.  
  380.         junk = AEInstallCoercionHandler(typeFalse, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  381.         MoreAssertQ(junk == noErr);
  382.  
  383.         junk = AEInstallCoercionHandler(typeQDPoint, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  384.         MoreAssertQ(junk == noErr);
  385.  
  386.         junk = AEInstallCoercionHandler(typeQDRectangle, typeText, (AECoercionHandlerUPP) gBasicToTextCoerceUPP, 0, false, false);
  387.         MoreAssertQ(junk == noErr);
  388.  
  389.         // Then install the coercions for lists, records, and things coercible
  390.         // to records.
  391.         
  392.         gRecordToTextCoerceUPP = NewAECoerceDescUPP(RecordToTextCoerceProc);
  393.         MoreAssertQ(gRecordToTextCoerceUPP != nil);
  394.  
  395.         junk = AEInstallCoercionHandler(typeAERecord, typeText, (AECoercionHandlerUPP) gRecordToTextCoerceUPP, 0, true, false);
  396.         MoreAssertQ(junk == noErr);
  397.  
  398.         junk = AEInstallCoercionHandler(typeAEList, typeText, (AECoercionHandlerUPP) gRecordToTextCoerceUPP, 0, true, false);
  399.         MoreAssertQ(junk == noErr);
  400.  
  401.         junk = AEInstallCoercionHandler(typeAppleEvent, typeText, (AECoercionHandlerUPP) gRecordToTextCoerceUPP, 0, true, false);
  402.         MoreAssertQ(junk == noErr);
  403.  
  404.         junk = AEInstallCoercionHandler(typeObjectSpecifier, typeText, (AECoercionHandlerUPP) gRecordToTextCoerceUPP, 0, true, false);
  405.         MoreAssertQ(junk == noErr);
  406.  
  407.         junk = AEInstallCoercionHandler(typeRangeDescriptor, typeText, (AECoercionHandlerUPP) gRecordToTextCoerceUPP, 0, true, false);
  408.         MoreAssertQ(junk == noErr);
  409.  
  410.         junk = AEInstallCoercionHandler(typeCompDescriptor, typeText, (AECoercionHandlerUPP) gRecordToTextCoerceUPP, 0, true, false);
  411.         MoreAssertQ(junk == noErr);
  412.  
  413.         junk = AEInstallCoercionHandler(typeLogicalDescriptor, typeText, (AECoercionHandlerUPP) gRecordToTextCoerceUPP, 0, true, false);
  414.         MoreAssertQ(junk == noErr);
  415.     }
  416.  
  417.     static const OSType kBBEditCreator = 'R*ch';
  418.  
  419.     static UInt32  gIndent;        // records the number of levels of indent, as controlled by BBLogIndent etc
  420.     static Boolean gLogging;    // determines whether logging is enabled (true) or not (false)
  421.     
  422.     extern pascal void BBLogStart(Boolean logging)
  423.         // See comment in header file.
  424.     {
  425.         OSStatus     err;
  426.         AppleEvent     theEvent;
  427.         AppleEvent     junkReply;
  428.         DescType     cDoc;
  429.  
  430.         // Start up by installing our coercion handles.  This enables the rest
  431.         // of the routines to provide much nicer output.
  432.         
  433.         InstallTextCoercionHandlers();
  434.         
  435.         gIndent = 0;
  436.         gLogging = logging;
  437.         if (gLogging) {
  438.         
  439.             // Send BBEdit an Apple event to make a new text window.  This
  440.             // will error if BBEdit is running, but the code to launch it
  441.             // is an unnecessary complication.  If you want to see logging,
  442.             // you have to have BBEdit running.
  443.             
  444.             MoreAENullDesc(&theEvent);
  445.             err = MoreAECreateAppleEventCreatorTarget(kAECoreSuite, kAECreateElement, kBBEditCreator, &theEvent);
  446.             if (err == noErr) {
  447.                 cDoc = cDocument;
  448.                 err = AEPutParamPtr(&theEvent, keyAEObjectClass, typeType, &cDoc, sizeof(cDoc));
  449.             }
  450.             if (err == noErr) {
  451.                 err = AESend(&theEvent, &junkReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
  452.                 if (err == connectionInvalid) {        // BBEdit not running
  453.                     err = noErr;
  454.                 }
  455.             }
  456.             MoreAEDisposeDesc(&theEvent);
  457.             MoreAssertQ(err == noErr);
  458.  
  459.         }
  460.     }
  461.     
  462.     extern pascal void BBLogSetState(Boolean logging)
  463.         // See comment in header file.
  464.     {
  465.         gLogging = logging;
  466.     }
  467.         
  468.     extern pascal void BBLogText(void *textPtr, Size textSize)
  469.         // See comment in header file.
  470.     {
  471.         OSStatus     err;
  472.         AppleEvent theEvent;
  473.         AppleEvent junkReply;
  474.  
  475.         MoreAssertQ(textPtr != nil);
  476.  
  477.         if (gLogging) {
  478.         
  479.             // Send BBEdit an insert text ('Nsrt') event.  The text will go
  480.             // into the front window, which is typically the window we created
  481.             // during BBLogStart.
  482.             
  483.             MoreAENullDesc(&theEvent);
  484.             err = MoreAECreateAppleEventCreatorTarget(kBBEditCreator, 'Nsrt', kBBEditCreator, &theEvent);
  485.             if (err == noErr) {
  486.                 err = AEPutParamPtr(&theEvent, keyDirectObject, typeText, textPtr, textSize);
  487.             }
  488.             if (err == noErr) {
  489.                 err = AESend(&theEvent, &junkReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout, nil, nil);
  490.                 if (err == connectionInvalid) {        // BBEdit not running
  491.                     err = noErr;
  492.                 }
  493.             }
  494.             MoreAEDisposeDesc(&theEvent);
  495.             MoreAssertQ(err == noErr);
  496.         }
  497.     }
  498.  
  499.     static void BuildLineHeader(Str255 str)
  500.     {
  501.         UInt32 dateTime;
  502.         Str255 tmpStr;
  503.         static Str255 indentStr = "\p                                                                                                  ";
  504.         
  505.         MoreAssertQ(str != nil);
  506.  
  507.         GetDateTime(&dateTime);
  508.         TimeString(dateTime, true, tmpStr, nil);
  509.         (void) PLstrcpy(str, tmpStr);
  510.         (void) PLstrcat(str, "\p ");
  511.         DateString(dateTime, shortDate, tmpStr, nil);
  512.         (void) PLstrcat(str, tmpStr);
  513.         (void) PLstrcat(str, "\p - ");
  514.         indentStr[0] = gIndent * 2;
  515.         (void) PLstrcat(str, indentStr);
  516.     }
  517.     
  518.     extern pascal void BBLogLine(ConstStr255Param str)
  519.         // See comment in header file.
  520.     {
  521.         Str255 tmpStr;
  522.  
  523.         MoreAssertQ(str != nil);
  524.  
  525.         if (gLogging) {
  526.             
  527.             // Add the date and time prefix, the appropriate indent, and the CR suffix.
  528.  
  529.             BuildLineHeader(tmpStr);
  530.             (void) PLstrcat(tmpStr, str);
  531.             (void) PLstrcat(tmpStr, "\p\r");
  532.  
  533.             BBLogText(&tmpStr[1], tmpStr[0]);
  534.         }
  535.     }
  536.     
  537.     extern pascal void BBLogIndent(void)
  538.         // See comment in header file.
  539.     {
  540.         gIndent += 1;
  541.     }
  542.     
  543.     extern pascal void BBLogOutdent(void)
  544.         // See comment in header file.
  545.     {
  546.         MoreAssertQ(gIndent > 0);
  547.         gIndent -= 1;
  548.     }
  549.     
  550.     extern pascal void BBLogOutdentWithErr(OSStatus errNum)
  551.         // See comment in header file.
  552.     {
  553.         Str255 tmpStr;
  554.         Str255 tmpStr2;
  555.         
  556.         BBLogOutdent();
  557.         if (gLogging) {
  558.             if (errNum == noErr) {
  559.                 (void) PLstrcpy(tmpStr, "\perr=noErr");
  560.             } else {
  561.                 (void) PLstrcpy(tmpStr, "\perr=");
  562.                 NumToString(errNum, tmpStr2);
  563.                 (void) PLstrcat(tmpStr, tmpStr2);
  564.             }
  565.             BBLogLine(tmpStr);
  566.         }
  567.     }
  568.  
  569.     extern pascal void BBLogAppleEvent(ConstStr255Param tag, const AppleEvent *theEvent)
  570.         // See comment in header file.
  571.     {
  572.         OSStatus     junk;
  573.         AEEventClass classID;
  574.         AEEventID    eventID;
  575.         DescType     junkType;
  576.         Size         junkSize;
  577.         Str255         tmpStr;
  578.         Str255         tmpStr2;
  579.         
  580.         MoreAssertQ(tag      != nil);
  581.         MoreAssertQ(theEvent != nil);
  582.  
  583.         if (gLogging) {
  584.         
  585.             // Get the Apple event class and event IDs.
  586.             
  587.             classID = '????';
  588.             junk = AEGetAttributePtr(theEvent, keyEventClassAttr, typeType, &junkType, &classID, sizeof(classID), &junkSize);
  589.             MoreAssertQ(junk == noErr);
  590.  
  591.             eventID = '????';
  592.             junk = AEGetAttributePtr(theEvent, keyEventIDAttr, typeType, &junkType, &eventID, sizeof(eventID), &junkSize);
  593.             MoreAssertQ(junk == noErr);
  594.  
  595.             // Create a log string out of those IDs.  Currently this
  596.             // is the only information about the event that we log;
  597.             // we might want to extend this in the future, with possibly
  598.             // the parameter list (or at least the direct object).
  599.             
  600.             (void) PLstrcpy(tmpStr, tag);
  601.             (void) PLstrcat(tmpStr, "\p='");
  602.             tmpStr2[0] = 4;
  603.             *((OSType *) &tmpStr2[1]) = classID;
  604.             (void) PLstrcat(tmpStr, tmpStr2);
  605.             (void) PLstrcat(tmpStr, "\p', '");
  606.             tmpStr2[0] = 4;
  607.             *((OSType *) &tmpStr2[1]) = eventID;
  608.             (void) PLstrcat(tmpStr, tmpStr2);
  609.             (void) PLstrcat(tmpStr, "\p'");
  610.  
  611.             BBLogLine(tmpStr);
  612.         }
  613.     }
  614.     
  615.     extern pascal void BBLogLong(ConstStr255Param tag,       SInt32 l)
  616.         // See comment in header file.
  617.     {
  618.         Str255 tmpStr;
  619.         Str255 tmpStr2;
  620.         
  621.         MoreAssertQ(tag != nil);
  622.  
  623.         if (gLogging) {
  624.             (void) PLstrcpy(tmpStr, tag);
  625.             (void) PLstrcat(tmpStr, "\p=");
  626.             NumToString(l, tmpStr2);
  627.             (void) PLstrcat(tmpStr, tmpStr2);
  628.  
  629.             BBLogLine(tmpStr);
  630.         }
  631.     }
  632.     
  633.     extern pascal void BBLogDesc(ConstStr255Param tag,       const AEDesc *desc)
  634.         // See comment in header file.
  635.     {
  636.         OSStatus     err;
  637.         AEDesc         coercedDesc;
  638.         Str255        descStr;
  639.         Str255        tmpStr;
  640.         Handle        descText;
  641.         
  642.         MoreAssertQ(tag  != nil);
  643.         MoreAssertQ(desc != nil);
  644.         
  645.         if (gLogging) {
  646.             MoreAENullDesc(&coercedDesc);
  647.             descText = nil;
  648.  
  649.             // First try coercing the descriptor to text.
  650.             // If we succeed, then we use the resulting textual
  651.             // description of the descriptor.  If we fail,
  652.             // we just log the descriptor type and size
  653.             
  654.             err = AECoerceDesc(desc, typeText, &coercedDesc);
  655.             if (err == noErr) {
  656.                 err = MoreAECopyDescriptorDataToHandle(&coercedDesc, &descText);
  657.                 
  658.                 // If the original descriptor type was text, add
  659.                 // some “ ” around the text.
  660.                 
  661.                 if (err == noErr && desc->descriptorType == typeText) {
  662.                     (void) Munger(descText, 0, nil, 0, "“", 1);
  663.                     err = MemError();
  664.                     
  665.                     if (err == noErr) {
  666.                         err = PtrAndHand("”", descText, 1);
  667.                     }
  668.                 }
  669.             } else {
  670.                 tmpStr[0] = 4;
  671.                 *((OSType *) &tmpStr[1]) = desc->descriptorType;
  672.                 (void) PLstrcpy(descStr, "\p'");
  673.                 (void) PLstrcat(descStr, tmpStr);
  674.                 (void) PLstrcat(descStr, "\p'");
  675.                 if (desc->dataHandle != nil) {
  676.                     (void) PLstrcat(descStr, "\p, size=");
  677.                     NumToString(AEGetDescDataSize(desc), tmpStr);
  678.                     (void) PLstrcat(descStr, tmpStr);
  679.                 }
  680.                 err = PtrToHand(&descStr[1], &descText, descStr[0]);
  681.             }
  682.  
  683.             // Now that descText is set up to contain the textual
  684.             // representation of desc, we just log it.  We also
  685.             // log the original type of the descriptor (in square
  686.             // brackets).
  687.             
  688.             // First prepend the line header and the tag.
  689.  
  690.             if (err == noErr) {
  691.                 BuildLineHeader(tmpStr);
  692.                 (void) PLstrcat(tmpStr, tag);
  693.                 (void) PLstrcat(tmpStr, "\p=");
  694.  
  695.                 (void) Munger(descText, 0, nil, 0, &tmpStr[1], tmpStr[0]);
  696.                 err = MemError();
  697.             }
  698.  
  699.             // Then append the original descriptor type and the CR.
  700.             
  701.             if (err == noErr) {
  702.                 (void) PLstrcpy(tmpStr, "\p [");
  703.                 descStr[0] = 4;
  704.                 *((OSType *) &descStr[1]) = desc->descriptorType;
  705.                 (void) PLstrcat(tmpStr, descStr);
  706.                 (void) PLstrcat(tmpStr, "\p]\x0d");
  707.                 
  708.                 err = PtrAndHand(&tmpStr[1], descText, tmpStr[0]);
  709.             }
  710.  
  711.             // Finally, log the text.
  712.             
  713.             if (err == noErr) {                
  714.                 HLock(descText);
  715.                 BBLogText(*descText, GetHandleSize(descText));
  716.             }
  717.  
  718.             // Clean up.
  719.             
  720.             if (descText != nil) {
  721.                 DisposeHandle(descText);
  722.                 MoreAssertQ(MemError() == noErr);
  723.             }
  724.             MoreAEDisposeDesc(&coercedDesc);
  725.         }
  726.     }
  727.     
  728.     extern pascal void BBLogDescType(ConstStr255Param tag,   DescType theType)
  729.         // See comment in header file.
  730.     {
  731.         Str255 tmpStr;
  732.         Str255 tmpStr2;
  733.  
  734.         MoreAssertQ(tag != nil);
  735.         
  736.         if (gLogging) {
  737.             (void) PLstrcpy(tmpStr, tag);
  738.             (void) PLstrcat(tmpStr, "\p='");
  739.             tmpStr2[0] = 4;
  740.             *((OSType *) &tmpStr2[1]) = theType;
  741.             (void) PLstrcat(tmpStr, tmpStr2);
  742.             (void) PLstrcat(tmpStr, "\p'");
  743.             
  744.             BBLogLine(tmpStr);
  745.         }
  746.     }
  747.  
  748. #endif // MORE_DEBUG
  749.